package com.whq.mvc.controller;

import com.google.common.util.concurrent.RateLimiter;
import com.whq.mvc.resp.BusinessException;
import com.whq.mvc.resp.CommonReturnType;
import com.whq.mvc.resp.EmBusinessError;
import com.whq.mvc.service.OrderService;
import com.whq.mvc.service.PromoService;
import com.whq.mvc.service.model.UserModel;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * 下单操作
 */

@RequestMapping("/order")
@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private PromoService promoService;

    private ExecutorService executorService;

    private RateLimiter rateLimiter;

    //初始化泄洪线程池
    @PostConstruct
    public void init() {
        executorService = Executors.newFixedThreadPool(20);
        rateLimiter = RateLimiter.create(300);
    }

    @PostMapping("/generatetoken")
    public CommonReturnType generateToken(@RequestParam(name = "itemId") Integer itemId,
                                          @RequestParam(name = "promoId") Integer promoId,
                                          HttpServletRequest request) throws BusinessException {
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "token参数未传，请重试");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if (Objects.isNull(userModel)) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户还未登入，不能下单");
        }
        String promoToken = promoService.generateSecondKillToken(promoId, itemId, userModel.getId());
        if (StringUtils.isEmpty(promoToken)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "生成秒杀令牌失败");
        }
        return CommonReturnType.create(promoToken);
    }

    @PostMapping("/createorder")
    public CommonReturnType createOrder(@RequestParam(name = "itemId") Integer itemId,
                                        @RequestParam(name = "amount") Integer amount,
                                        @RequestParam(name = "promoId", required = false) Integer promoId,
                                        @RequestParam(name = "promoToken", required = false) String promoToken,
                                        HttpServletRequest request) throws BusinessException {
        if (!rateLimiter.tryAcquire()){
            throw new BusinessException(EmBusinessError.RATELIMIT);
        }

        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "token参数未传，请重试");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if (Objects.isNull(userModel)) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户还未登入，不能下单");
        }

        //校验秒杀令牌是否正确
        if (promoId != null) {
            String tokenKey = String.format("promo_token_%s_userId_%s_itemId_%s", promoId, userModel.getId(), itemId);
            String cachePromoToken = (String) redisTemplate.opsForValue().get(tokenKey);
            if (StringUtils.isEmpty(cachePromoToken) || !StringUtils.equals(promoToken, cachePromoToken)) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "秒杀令牌校验失败");
            }
        }

        if (redisTemplate.hasKey("promo_item_stock_invalid_" + itemId)) {
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }

        //保证只有20个线程请求处理，其他会加入阻塞队列等待
        //主要是保证假设令牌数量过大，下单接口流量可控
        Future<Object> future = executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                orderService.createOrder(userModel.getId(), itemId, promoId, amount);
                return null;
            }
        });

        try {
            future.get();   //阻塞等待下单结果返回
        } catch (InterruptedException e) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        } catch (ExecutionException e) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }
        return CommonReturnType.create(null);
    }
}
